package com.dapeng.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

public class MainCase {

    public static final int PORT = 9090;

    public static void main(String[] args) throws Exception {
        System.out.println("请输入交互方式：BIO 或是 NIO:\n");
        String type = args[0];
        switch (type) {
            case "bio":
                bio_test();
                break;
            case "nio":
                nio_test();
                break;
            case "select":
                select_test();
                break;
            default:
                System.out.println("not support type");
                break;
        }
    }

    public static void bio_test() throws Exception {
        ServerSocket socket = new ServerSocket(PORT, 20);
        System.out.println("服务端启动：" + PORT);
        while (true) {
            Socket client = socket.accept();

            if (client == null) {
                break;
            }
            String clientInfo = client.getInetAddress() + ":" + client.getPort();
            new Thread(() -> {
                while (true) {
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
                        String readLine = reader.readLine();
                        if (readLine != null) {
                            System.out.println("读取客户端数据：%s : %s".formatted(clientInfo, readLine));
                        }
                        //reader.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    public static void nio_test() throws Exception {
        LinkedList<SocketChannel> clients = new LinkedList<>();

        ServerSocketChannel serverSocket = ServerSocketChannel.open();

        serverSocket.bind(new InetSocketAddress(PORT));

        System.out.println("服务端启动：" + PORT);
        serverSocket.configureBlocking(false);

        while (true) {
            SocketChannel client = serverSocket.accept();
            if (client != null) {
                client.configureBlocking(false);

                System.out.println("与客户端建立连接：" + getRemoveAddr(client));
                clients.add(client);
            }

            ByteBuffer buffer = ByteBuffer.allocateDirect(64);

            for (SocketChannel c : clients) {
                readContentFromBuffer(c, buffer);
            }
        }
    }


    public static void select_test() throws Exception {
        ServerSocketChannel server = ServerSocketChannel.open();

        server.bind(new InetSocketAddress(PORT));
        server.configureBlocking(false);

        Selector selector = Selector.open();
        server.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("服务端启动：" + PORT);

        while (true) {
            Set<SelectionKey> keys = selector.keys();

            while (selector.select() > 0) {
                Set<SelectionKey> set = selector.selectedKeys();

                Iterator<SelectionKey> iterator = set.iterator();

                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    if (key.isAcceptable()) {
                        acceptHandler(key);
                    } else if (key.isReadable()) {
                        readHandler(key);
                    }
                }
            }
        }
    }

    private static void acceptHandler(SelectionKey key) throws Exception {
        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
        SocketChannel client = channel.accept();

        client.configureBlocking(false);
        ByteBuffer buffer = ByteBuffer.allocateDirect(64);

        client.register(key.selector(), SelectionKey.OP_READ, buffer);
        System.out.println("与客户端建立连接：" + getRemoveAddr(client));
    }

    private static void readHandler(SelectionKey key) throws Exception {
        SocketChannel channel = (SocketChannel) key.channel();

        ByteBuffer buffer = (ByteBuffer) key.attachment();

        readContentFromBuffer(channel, buffer);
    }

    private static void readContentFromBuffer(SocketChannel channel, ByteBuffer buffer) throws IOException {
        int num = channel.read(buffer);
        if(num > 0) {
            buffer.flip();
            byte[] bytes = new byte[buffer.limit()];
            buffer.get(bytes);

            String content = new String(bytes);
            System.out.println("read content：" + content);

        }
    }

    private static String getRemoveAddr(SocketChannel channel) {
        return channel.socket().getInetAddress().getHostAddress() + ":" + channel.socket().getPort();
    }
}
